#pragma once

#include "Error.h"
#include "Log.h"
#include "Socket.h"
#include <iostream>
#include <string>
#include <cstring>
#include <cerrno>
#include <functional>
#include <unistd.h>
#include <poll.h>

namespace poll_server {
    class PollServer {
    public:
        constexpr static int defaultPort = 18989; // 服务器的默认端口号
        constexpr static int fdNums = 2048; //* 自定义数值，解决了select中能够托管的fd受系统限制的缺点，现在能够自定义了
        constexpr static int defaultFd = -1; // fd的默认数值
        constexpr static int receiveBufferSize = 1024; // 接收数据的缓存区大小

    private:
        using func_t = std::function<std::string (const std::string&)>;

    private:
        uint16_t _port;
        int _listen_sockfd;
        func_t _handle_method; // 对recv上来的信息的处理方法
        struct pollfd* _fds_array; // 存储需要poll帮忙等待的有效sockfd

    public:
        //@param port defaultPort = 18989
        PollServer(const func_t& handle_method, const uint16_t& port = defaultPort)
            : _port(port)
            , _listen_sockfd(defaultFd)
            , _handle_method(handle_method)
            , _fds_array(new struct pollfd[fdNums])
        {
            for (int i = 0; i < fdNums; ++i) {
                initPollMember(i);
            }
        }

        ~PollServer() {
            if (_listen_sockfd != defaultFd) {
                close(_listen_sockfd);
            }
            if (_fds_array != nullptr) {
                delete[] _fds_array;
                _fds_array = nullptr;
            }
        }

    public:
        // 初始化服务器
        void initServer() {
            _listen_sockfd = Socket::getSocket(); // 获得监听套接字
            Socket::bindSocket(_listen_sockfd, _port); // 绑定监听套接字
            Socket::listenSocket(_listen_sockfd); // 设置监听状态

            //task 将listen套接字放进poll中，让poll帮忙经历accept的等待过程(输入事件)
            _fds_array[0].fd = _listen_sockfd;
            _fds_array[0].events = POLLIN;

            logMessage(NORMAL, "initServer success!");
        }

        void run() {
            int timeout = -1; // poll的阻塞等待时长(单位ms)，小于0为阻塞，等于0为非阻塞，大于零为先阻塞设定的时长再非阻塞
            for (;;) {
                int n = poll(_fds_array, fdNums, timeout);
                if (n > 0) { // 成功等待
                    logMessage(NORMAL, "select wait success!");
                    handleReadEvents();
                }
                else if (n == 0) { // 超时返回，处理非阻塞或计时阻塞select没等到输入事件的情况
                    logMessage(NORMAL, "time out...");
                }
                else { // n < 0, select出错了
                    logMessage(ERROR, "select error occur, code: %d, description: %s", errno, strerror(errno));
                }
            }
        }

    private:
        // 展示_fds_array中有效的fd
        void showFds() {
            std::cout << "_fds_array contains: ";
            for (int i = 0; i < fdNums; ++i) {
                if (_fds_array[i].fd != defaultFd) {
                    std::cout << _fds_array[i].fd << " ";
                }
            }

            std::cout << std::endl;
        }

        /**
         * 获取一个新连接
         ** 这个函数是在select为listen套接字成功等待之后才会调用
         ** 此时系统接口accept不会通过阻塞来等待连接，而是直接获取一个新连接
        */
        void acceptLink() {
            std::string client_ip;
            uint16_t client_port;
            int sockfd = Socket::acceptSocket(_listen_sockfd, &client_ip, &client_port);

            if (sockfd < 0) { // 获取新连接失败
                logMessage(ERROR, "accept a new link err!");
                return;
            }

            logMessage(NORMAL, "accept a new link success sockfd: %d, client_ip: %s, client_port: %d", sockfd, client_ip.c_str(), client_port);

            /*
            * 我们在成功获取到新连接后，不能像以前一样直接recv或read，
            * 因为新连接不一定发送数据过来了，又因为该程序为但进程(线程)，所以就会阻塞在这里
            * 那么就和之前最普通的tcp服务器一样了
            * 所以我们要做的是将新获取的fd放进_fds_array中，让select替recv和read等待
            */

           //task 在_fds_array中找空位置，有的话就将sockfd放进去，没有的话就关闭连接
           int i = 0;
            for (; i < fdNums; ++i) {
                if (_fds_array[i].fd == defaultFd) {
                    break;
                }
            }
            if (i == fdNums) { // 没有空位置
                logMessage(WARNING, "server is crowded, please wait some time");
                close(sockfd);
                return;
            }
            else {
                //task 将新连接的输入事件放进poll中托管
                _fds_array[i].fd = sockfd;
                _fds_array[i].events = POLLIN;
                _fds_array[i].revents = 0;

                logMessage(NORMAL, "load sockfd %d into _fds_array[%d] success!", sockfd, i);
                showFds();
            }
        }

        /**
         * 从有输入事件的套接字中获取消息
         * 
         * @param sockfd 有输入事件的套接字
         * @param pos sockfd在_fds_array中的下标, 便于从_fds_array找到并删除sockfd
        */
        void receiveMessage(const int& sockfd, const int& pos) {
            char buffer[receiveBufferSize] = {};
            ssize_t s = recv(sockfd, buffer, receiveBufferSize - 1, 0);
            if (s > 0) { // 成功读取到数据
                buffer[s] = '\0';
                logMessage(NORMAL, "receive message %s success!", buffer);
            }
            else if (s == 0) { // 读到文件结尾EOF(客户端关闭)
                logMessage(NORMAL, "client %d quit", sockfd);
                resetPollMember(pos);
                return;
            }
            else {
                resetPollMember(pos);
                logMessage(ERROR, "Unknown error occurred in receiveMessage: %s", strerror(errno));
                return;
            }

            //task 处理recv上来的数据
            std::string response(_handle_method(std::string(buffer)));
            logMessage(NORMAL, "handle data success, response: %s", response.c_str());

            //task 将处理完的数据发送回去
            send(sockfd, response.c_str(), response.size(), 0);
            logMessage(NORMAL, "send response to client success");
        }

        /**
         * 处理已经准备好的输入事件(可能存在多个)
         * 
         * @param read_fds 存储有输入事件的fd的位图
        */
        void handleReadEvents() {
            for (int i = 0; i < fdNums; ++i) {
                if (_fds_array[i].fd == defaultFd) {
                    continue; // 过滤掉非法的sockfd(不存在或不需要poll管理)
                }

                if (!(_fds_array[i].events & POLLIN)) { // 过滤掉不需要poll托管输入事件的fd
                    continue;
                }

                if (_fds_array[i].revents & POLLIN) { // 保证_fds_array[i]有输入事件准备完成
                    if (_fds_array[i].fd == _listen_sockfd) {
                        // listen到了一个新连接
                        acceptLink();
                    }
                    else {
                        receiveMessage(_fds_array[i].fd, i);
                    }
                }
            }

            logMessage(NORMAL, "handleReadEvents success!");
        }

        // 初始化poll中特定位置的内容
        void initPollMember(const int& pos) {
            _fds_array[pos].fd = defaultFd;
            _fds_array[pos].events = 0;
            _fds_array[pos].revents = 0;
        }

        // 重置poll中特定位置的内容，并关闭其中的fd
        void resetPollMember(const int& pos) {
            if (_fds_array[pos].fd != defaultFd) {
                close(_fds_array[pos].fd);
            }

            initPollMember(pos);
        }

    }; //@end class SelectServer
} //@end select_server